
#import "EMErrorDescription.h"
#import "EMErrorInfo.h"
#import "EMErrorManager.h"
#import <appkit/appkit.h>

#define MAX_FUNCTION_ARGS  3		// Print at most three args to a function
#define MAX_FRAMES 50


typedef struct _type  
	{
	char	encoding;
	char	*format;
	char	*name;
	} ParType;


@implementation EMErrorInfo:Object

//--------------------------------------------------------------------------------
// GLOBALE VARIABLEN (mehr der Uebersicht halber hier)
//--------------------------------------------------------------------------------

static ParType encodings[] = {	
			{_C_ID,			"0x%06lx",		"id"},
			{_C_CLASS,		"0x%06lx",		"Class"},
			{_C_SEL,		"@selector(%s)","SEL"},
			{_C_CHR,		"%c",			"char"},
			{_C_UCHR,		"%c",			"unsigned char"},
			{_C_SHT,		"%s",			"short"},
			{_C_USHT,		"%c",			"unsigned short"},
			{_C_INT,		"%d",			"int"},
			{_C_UINT,		"%d",			"unsigned int"},
			{_C_LNG,		"%l",			"long"},
			{_C_ULNG,		"%l",			"unsigned long"},
			{_C_FLT,		"%f",			"float"},
			{_C_DBL,		"%f",			"double"},
			{_C_VOID,		"0x%06lx",		"void"},
			{_C_PTR,		"0x%06lx",		"ptr"},
			{_C_CHARPTR,	"\"%s\"",		"char *"},
			{_C_STRUCT_B,	"%x",			"struct"},
			{0,				"0x%06lx",		"unknown type"}};


//--------------------------------------------------------------------------------
// INITIALIZER
//--------------------------------------------------------------------------------

+ newWithCode:(int)ncode userInfo:(const void *)a:(const void *)b;
	{
	return [[EMErrorInfo alloc] initWithCode:ncode userInfo:a:b];
	}


+ newWithCode:(int)ncode userInfo:(const void *)a:(const void *)b
	file:(const char *)loc line:(int)line;
	{
	return [[EMErrorInfo alloc] initWithCode:ncode userInfo:a:b 
					file:loc line:line];
	}


//--------------------------------------------------------------------------------
// INIT & FREE
//--------------------------------------------------------------------------------

- initWithCode:(int)ncode userInfo:(const void *)a:(const void *)b;	
	{
	return [self initWithCode:ncode userInfo:a:b file:"?" line:0];
	}
	

- initWithCode:(int)ncode userInfo:(const void *)a:(const void *)b
	file:(const char *)loc line:(int)line;
	{
	char	buf[MAXPATHLEN+1]="?";
	
	[super init];
	if(line>0)
		sprintf(buf,"%s %d",loc,line);
	code=ncode;
	location=NXCopyStringBufferFromZone(buf,[self zone]);
	stackFrame=NULL;
	userData1=a;
	userData2=b;
	time(&timet);
	message=timeStamp=NULL;
	return self;
	}


- free
	{
	NXZoneFree([self zone],location);
	if(timeStamp)
		NXZoneFree([self zone],timeStamp);
	if(message)
		NXZoneFree([self zone],message);
	if(stackFrame)
		NXZoneFree([self zone],stackFrame);
	if(userData1)
		{
		if(userData1Free=='s')
			NX_FREE((void *)userData1);
		else if(userData1Free=='o')
			[(id)userData1 free];
		}
	if(userData2)
		{
		if(userData2Free=='s')
			NX_FREE((void *)userData2);
		else if(userData2Free=='o')
			[(id)userData2 free];
		}
	return [super free];
	}


//--------------------------------------------------------------------------------
// I-VAR
//--------------------------------------------------------------------------------

- setUDFreeSemantics:(const char *)desc;
	{
	userData1Free=desc[0];
	userData2Free=desc[1];
	return self;
	}
	

//--------------------------------------------------------------------------------
// I-VAR
//--------------------------------------------------------------------------------

- (int)code;
	{
	return code;
	}
	

- (time_t)time;
	{
	return timet;
	}
	

- (const char *)location;
	{
	return location;
	}
	

- (const char *)stack;
	{
	return stackFrame;
	}


- (const void *)userData1;
	{
	return userData1;
	}
	
	
- (const void *)userData2;
	{
	return userData2;
	}
	

//--------------------------------------------------------------------------------
// Convenience
//--------------------------------------------------------------------------------

- (const char *)message;
	{
	if(!message)
		{
		char buf[1000];
		
		sprintf(buf,[[ERROR_MANAGER errorDescriptionFor:code] msg],
						userData1,userData2);
		message=NXCopyStringBufferFromZone(buf,[self zone]);
		}
	return message;
	}


- (const char *)timeStamp;
	{
	if(!timeStamp)
		{
		char buf[90];
		
		strftime(buf,89,"%d %b %y %H:%M:%S %Z",localtime(&timet));
		timeStamp=NXCopyStringBufferFromZone(buf,[self zone]);
		}
	return timeStamp;
	}
	

- (NXAtom)exceptionClass;
	{
	return [[ERROR_MANAGER errorDescriptionFor:code] exceptionClass];
	}


- (const char *)codeString;
	{
	static char string[20];
	
	switch([self source])
		{
	case EMSourceApplication: sprintf(string,"A%d",code-EM_APPBASE); break;
	case EMSourceSignal: 	  sprintf(string,"S%d",code-EM_SIGBASE); break;
	case EMSourceInternal:	  sprintf(string,"I%d",code-EM_INTBASE); break;
	case EMSourceKit:		  sprintf(string,"K%d",code);			 break;
		}
	return string;
	}


- (EMErrorSource)source;
	{
	if(code>=EM_APPBASE)
		return EMSourceApplication;
	else if(code>=EM_SIGBASE)
		return EMSourceSignal;
	else if(code>=EM_INTBASE)
		return EMSourceInternal;
	return EMSourceKit;
	}

	
//--------------------------------------------------------------------------------
// Hilfsmethoden, um den Kontext zu speichern zu koennen.
//--------------------------------------------------------------------------------
//		ASSUMPTION:  The layout of a stack frame for a method invocation is:
//			fp+0 bytes:		calling frame
//			fp+4 bytes:		calling pc
//			fp+8 bytes:		self
//			fp+12 bytes:	selector for method invoked
//			fp+16 bytes:	first (overt) argument
//
//		ASSUMPTION: The layout of a stack frame for a function invocation is:
//			fp+0 bytes:		calling frame
//			fp+4 bytes:		calling pc
//			fp+8 bytes:		first argument
//
//		Clearly these are shady assumptions, however we're already
//		in the process of crashing, so what harm can be done?
//--------------------------------------------------------------------------------


- printFunctionFromFP:(void *)framePointer into:(char *)buffer
	{
	char	argStrg[20];
	void	*argStart;
	long	argNum;	// Index into arguments;
	
	strcat(buffer,"function(");
	argStart=framePointer+8;
	for(argNum=0;argNum<MAX_FUNCTION_ARGS;argNum++)  
		{
		sprintf(argStrg,"%s0x%06lx",
				((argNum>0)?", ":""),*(((unsigned long *)argStart)+argNum));
		strcat(buffer,argStrg);
		}
	strcat(buffer,")\n");
	return self;
	}


- printMethodFromFP:(void *)framePointer into:(char *)buffer
	{
	char 	tmp[256],selStrg[200],*selPart;
	SEL 	selector;
	id 		object;
	Method 	m;
	BOOL 	isClassMethod;

	object=*(id *)(framePointer+8);  	 // receiver is 8 bytes from fp
	selector=*(SEL *)(framePointer+12);  // selector is 12 bytes from fp
	isClassMethod=([object class]==object);
	
	sprintf(tmp,"%c[%s",(isClassMethod?'+':'-'),object_getClassName(object));
	strcat(buffer,tmp);
	strcpy(selStrg,sel_getName(selector));
	
	if(isClassMethod)
		m=class_getClassMethod([object class],selector);
	else
		m=class_getInstanceMethod([object class],selector);
	
	if(m)
		{
		void 	*argStart=(framePointer+8);
		int 	argNum,argCount,offset;
		char	*type;
		
		argCount=method_getNumberOfArguments(m);
		if(argCount==2)
			{
			strcat(buffer," ");
			strcat(buffer,selStrg);
			}
		else
			for(argNum=2,selPart=strtok(selStrg,":");
				argNum<argCount;
				argNum++,selPart=strtok(NULL,":"))		
				{
				ParType *cur;
				long	arg;
				
				method_getArgumentInfo(m,argNum,&type,&offset);
				arg=*(long *)(argStart+offset);
				for (cur=encodings;cur->encoding;cur++)
					if (cur->encoding == type[0])
						break;
				if(selPart)
					sprintf(tmp," %s:(%s)",selPart?selPart:"",cur->name);
				else
					sprintf(tmp,":(%s)",cur->name);
				strcat(buffer,tmp);
				if(arg==0 && type[0]==_C_SEL)
					sprintf(tmp,"{unknown method}");
				else
					sprintf(tmp,cur->format,arg);
				strcat(buffer,tmp);
				}
		strcat(buffer,"]\n");
		}  
	else
		{
		sprintf(tmp," %s] {unknown method}\n",selStrg);
		strcat(buffer,tmp);
		}
	return self;
	}


- dumpBacktraceInto:(char *)buf;
	{
	void 			*framePointer;	// pointer to current frame
	unsigned int 	frameCount;		// counter for number of frames printed
		
	
	framePointer=((void *)&self)-8;	// Start the frame pointer off at our frame
	framePointer=(void *)*(long *)framePointer;  // go up one frame
	frameCount=0;
	
	// Assume that a whole lotta frames means either (a) we're trashed or
	// (b) we're in a recursive deathtrap.  In the latter case, we've
	// probably got enough info to see a whole cycle.
	
	while(frameCount<MAX_FRAMES && framePointer)
		{ // If this frame is a method call we'll have a valid selector at (fp+12).
		if (sel_isMapped(*(SEL *)(framePointer+12))) 
			[self printMethodFromFP:framePointer into:buf];
		else
			[self printFunctionFromFP:framePointer into:buf];
		frameCount++;
		framePointer=(void *)*(long *)framePointer;  // go up one frame
		}
	return self;
	}


- getContext;
	{
	char 	*buf;
	
	time(&timet);
#if m68k || i386
	buf=NXZoneCalloc([self zone],20*1024,sizeof(char));
	[self dumpBacktraceInto:buf];
	stackFrame=NXZoneRealloc([self zone],buf,strlen(buf+1));
#elif hppa || sparc
	stackFrame=NXCopyStringBufferFromZone("<<Can't dump stack on "
			"architecture " __ARCHITECTURE__ " yet>>\n",[self zone]);
#else
	stackFrame=NXCopyStringBufferFromZone("<<Can't dump stack on "
			"unknown architecture " __ARCHITECTURE__ ">>\n",[self zone]);
#endif
	return self;	
	}


//--------------------------------------------------------------------------------
// THAT'S IT
//--------------------------------------------------------------------------------

@end
